package gnss_plugs

import (
	"dgo/goutils/gnss"
	"dgo/goutils/utils"
	"fmt"
	"io"
	"os"
	"time"
)

// 历元缓存
type gnssRinexObs3xWriterEpochCacheRec struct {
	rinexHeader  *gnss.RinexHeader
	ObsFileName  string
	BeginObsTime time.Time
	epochList    *utils.SyncQueue
}

func newEpochCacheRec(cache_n int) *gnssRinexObs3xWriterEpochCacheRec {
	rval := &gnssRinexObs3xWriterEpochCacheRec{}
	rval.epochList = utils.NewSyncQueue()
	return rval
}

// 观测值写入者
type gnssRinexObs3xWriterIOWriter struct {
	rinexHeader  *gnss.RinexHeader
	IO           io.WriteCloser
	realfilename string
	write_size   int64
	lastWrite_t  time.Time
}

type gnssRinexObs3xWriterBase struct {
	Filefmt         string
	lastWringWaring string

	//  写入文件时, 如果rinex数据超过该值则 会写入一次文件
	writeCacheBlockSize uint64

	AppendIfFileExist byte

	MarkerName                                  string  // Name of antenna marker A60
	Observer, Agency                            string  // OBSERVER / AGENCY Name of Observer / Agency A20,A40
	X, Y, Z                                     float64 // APPROX POSITION XYZ
	AntennaDeltaH, AntennaDeltaE, AntennaDeltaN float64 // ANTENNA: DELTA H/E/N
	ReceiverNo, ReceiverType, ReceiverVer       string  // REC # / TYPE / 3A20 VERS Receiver number, type, and version (Version:e.g. Internal Software Version)	//4502587             SEPT POLARX5        5.3.2
	AntNumber, AntType                          string  // ANT # / TYPE (2A20)  Antenna number and type

	writerMap utils.GSyncMapGroup // 文件名->真正的文件名

	// 最后写入开始的时间
	lastWriteCacheT    time.Time
	lastWriteFileSuccT time.Time // 最后写入文件成功的时间
	lastWriteInfo      string    // 最后写入统计信息
	lastWriteStep      string
	writefile_size     int64 // 写入文件的字节数

}

/*
*
根据obsFile获取Writer对象
*/
func (this *gnssRinexObs3xWriterBase) checkWriter(rec *gnssRinexObs3xWriterEpochCacheRec) *gnssRinexObs3xWriterIOWriter {
	utils.ForceCreateFilePath(rec.ObsFileName)
	w := this.writerMap.LoadOrStoreFunc(rec.ObsFileName, func() interface{} {
		rval := &gnssRinexObs3xWriterIOWriter{}
		return rval
	}, nil).(*gnssRinexObs3xWriterIOWriter)
	if w.IO == nil { // IO不存在, 尝试打开文件
		if len(w.realfilename) > 0 { // 旧的文件
			f1, err := os.OpenFile(w.realfilename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
			if err != nil {
				w.realfilename = ""
			} else {
				w.IO = f1
				return w
			}
		}

		// 追加文件后面
		if this.AppendIfFileExist == 1 && utils.FileIsExists(rec.ObsFileName) {
			f1, err := os.OpenFile(rec.ObsFileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
			if err != nil {
				w.realfilename = ""
			} else {
				w.realfilename = rec.ObsFileName
				w.IO = f1
				w.write_size, _ = utils.FileSize(rec.ObsFileName)
				return w
			}
		}

		if len(w.realfilename) == 0 { // 新文件

			f1, fname, err := utils.CreateIfExistsThenRename(rec.ObsFileName)
			if err != nil {
				return nil
			}
			w.realfilename = fname
			w.IO = f1
			w.write_size = 0
			return w
		}
	}
	return w

}

func (this *gnssRinexObs3xWriterBase) writeRinexHeader2Writer(w io.Writer, temp_signHeader string, temp_starttime, end_time time.Time) error {
	// first line
	var sb utils.BytesBuilder
	sb.AppendStrSuffixForWidth(gnss.PrintRinexFV(3.04, 2, 9), 20, " ").
		AppendStrSuffixForWidth("OBSERVATION DATA", 20, " ").
		AppendStrSuffixForWidth("M (MIXED)", 20, " ").
		WriteString("RINEX VERSION / TYPE\n")

	sb.AppendStrSuffixForWidth(RINEX_TransVer, 20, " ").
		AppendStrSuffixForWidth(RINEX_CopyRight, 20, " ").
		AppendStrSuffixForWidth(temp_starttime.Format("20060102 150405")+" UTC", 20, " ").
		WriteString("PGM / RUN BY / DATE\n")

	sb.AppendStrSuffixForWidth(this.MarkerName, 60, " ").WriteString("MARKER NAME\n")
	sb.AppendStrSuffixForWidth(this.Observer, 20, " ").AppendStrSuffixForWidth(this.Agency, 40, " ").WriteString("OBSERVER / AGENCY\n")
	sb.AppendStrSuffixForWidth(this.ReceiverNo, 20, " ").AppendStrSuffixForWidth(this.ReceiverType, 20, " ").
		AppendStrSuffixForWidth(this.ReceiverVer, 20, " ").WriteString("REC # / TYPE / VERS\n")
	sb.AppendStrSuffixForWidth(this.AntNumber, 20, " ").AppendStrSuffixForWidth(this.AntType, 20, " ").
		AppendStrSuffixForWidth("", 20, " ").WriteString("ANT # / TYPE\n")

	sb.AppendRinexV(this.X, 4, 14, false).AppendRinexV(this.Y, 4, 14, false).AppendRinexV(this.Z, 4, 14, false).
		AppendStrSuffixForWidth("", 18, " ").WriteString("APPROX POSITION XYZ\n")

	sb.AppendRinexV(this.AntennaDeltaH, 4, 14, false).AppendRinexV(this.AntennaDeltaE, 4, 14, false).AppendRinexV(this.AntennaDeltaN, 4, 14, false).
		AppendStrSuffixForWidth("", 18, " ").WriteString("ANTENNA: DELTA H/E/N\n")

	sb.WriteString(temp_signHeader)
	if !temp_starttime.IsZero() {
		sb.WriteString("  " + temp_starttime.Format("2006    01    02    15    04   05") + ".0000000     GPS         TIME OF FIRST OBS\n")
	}
	if !end_time.IsZero() {
		sb.WriteString("  " + end_time.Format("2006    01    02    15    04   05") + ".0000000     GPS         TIME OF LAST OBS\n")
	}
	sb.AppendStrSuffixForWidth("", 60, " ").WriteString("END OF HEADER\n")

	_, err := w.Write(sb.Bytes())
	return err
}

func (this *gnssRinexObs3xWriterBase) tryWriteBlock(w *gnssRinexObs3xWriterIOWriter, rec *gnssRinexObs3xWriterEpochCacheRec) bool {
	var sb utils.BytesBuilder
	if w.write_size == 0 {
		signHeaderStr := w.rinexHeader.PrintSignHeader()
		err := this.writeRinexHeader2Writer(&sb, signHeaderStr, rec.BeginObsTime, utils.ZeroTime)
		if err != nil {
			this.lastWringWaring = fmt.Sprintf("[%s]写入Header异常:%s", utils.NowString(), err.Error())
			return false
		}
	}

	var err error

	var innerwritefunc = func(buf []byte) error {

		n1, e1 := w.IO.Write(buf)
		if e1 != nil {
			return e1
		}

		w.write_size += int64(n1)
		w.lastWrite_t = time.Now()
		this.lastWriteFileSuccT = time.Now()
		this.writefile_size += int64(n1)
		return nil
	}

	// 循环历元
	rec.epochList.Range(func(idx int, value interface{}, removeit *bool) bool {
		ep := value.(*gnss.RtcmMsmEpoch)
		var sb1 utils.BytesBuilder
		n := ep.WriteRinex3String(w.rinexHeader, &sb1)
		sb.AppendStrs("> ", ep.EpochTime.Format("2006 01 02 15 04 05.0000000")+"  0 "+fmt.Sprintf("%.2d", n)).AppendStr("\n")
		if n == 0 {
			this.lastWringWaring = fmt.Sprintf("[%s]未生成RINEX数据", utils.NowString())
		} else {
			sb.Write(sb1.Bytes())
		}
		if sb.Len() >= int(this.writeCacheBlockSize) {
			e1 := innerwritefunc(sb.Bytes())
			if e1 != nil {
				err = e1
				return false
			}
			sb.Reset()
		}
		return true
	})

	if sb.Len() > 0 {
		e1 := innerwritefunc(sb.Bytes())
		if e1 != nil {
			err = e1
		}
	}

	if err != nil {
		this.lastWringWaring = fmt.Sprintf("[%s]写入数据异常:%s", utils.NowString(), err.Error())
		return false
	}

	return err == nil
}
